package comm_ctrl

import (
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"
	"ubt-aio-service/app/pkg/code"
	"ubt-aio-service/app/pkg/conf"
	"ubt-aio-service/app/pkg/logger"
	"ubt-aio-service/app/pkg/parse"
	"ubt-aio-service/app/serv/comm_serv"
	"ubt-aio-service/app/utils"
)

type comm struct{}

var CommCtrl = comm{}

// NumCode 生成数字的图形验证码
func (c *comm) NumCode(r *http.Request, w http.ResponseWriter) {
	res := code.NewImg(r, 70, 38)
	utils.ToJson(w, res)
}

// Areas 下拉选择省市区乡镇
func (c *comm) Areas(r *http.Request, w http.ResponseWriter) {
	// 过滤关键参数值中的非法字符
	u := r.URL.Query()
	if u == nil {
		utils.ToJson(w, utils.ParamErr)
		return
	}
	pid := u.Get("pid")
	pid = utils.InputFilter(pid, true)
	if pid == "" {
		utils.ToJson(w, utils.ParamErr)
		return
	}
	res := comm_serv.CommServ.Areas(pid)
	utils.ToJson(w, res)
}

// Upload 上传文件，不保存至数据库，不进行用户校验
func (c *comm) Upload(r *http.Request, w http.ResponseWriter) {
	//从表单中读取文件
	file, header, err := r.FormFile("upFile")
	if err != nil {
		logger.Error("文件获取失败。" + err.Error())
		utils.ToJson(w, utils.UpfileErr)
		return
	}
	var fileName, extName, dateStr, basePath, baseUrl, filePath, dstPath, thumbPath, vstUrl, thumbUrl string
	basePath = conf.Cfg.AppConf.FilePath + "/file/"
	baseUrl = conf.Cfg.AppConf.FileUrl + "/file/"
	dateStr = time.Now().Format("200601")
	fileName = header.Filename
	extName = path.Ext(fileName)
	extName = strings.ToLower(extName)
	fileName = fileName[:(len(fileName) - len(extName))]
	var maxSize int64 = 500 * 1024 * 1024
	if !strings.Contains(".jpg.png.gif.bmp.jpeg.txt.xls.xlsx.doc.docx.pdf.zip.7z.rar.mp3.wav.wma.mp4.mpeg", extName) {
		utils.ToJson(w, utils.NotAllow)
		return
	}
	if strings.Contains(".jpg.png.gif.bmp.jpeg", extName) {
		maxSize = 5 * 1024 * 1024
		filePath = basePath + "img/src/" + dateStr + "/"
		dstPath = basePath + "img/" + dateStr + "/"
		thumbPath = basePath + "img/thumb/" + dateStr + "/"
		vstUrl = baseUrl + "img/" + dateStr + "/"
		thumbUrl = baseUrl + "img/thumb/" + dateStr + "/"
		// 创建源文件的目录
		if _, err = os.Stat(dstPath); err != nil || os.IsNotExist(err) {
			if err = os.Mkdir(dstPath, os.ModePerm); err != nil {
				logger.Error("上传目录创建失败。" + err.Error())
				utils.ToJson(w, utils.UpPathErr)
				return
			}
		}
		// 创建缩略图的目录
		if _, err = os.Stat(thumbPath); err != nil || os.IsNotExist(err) {
			if err = os.Mkdir(thumbPath, os.ModePerm); err != nil {
				logger.Error("上传目录创建失败。" + err.Error())
				utils.ToJson(w, utils.UpPathErr)
				return
			}
		}
	} else if strings.Contains(".xls.xlsx.doc.docx.pdf.pptx.ppt.txt.md", extName) {
		filePath = basePath + "doc/" + dateStr + "/"
		vstUrl = baseUrl + "doc/" + dateStr + "/"
	} else if strings.Contains(".zip.7z.rar", extName) {
		filePath = basePath + "zip/" + dateStr + "/"
		vstUrl = baseUrl + "zip/" + dateStr + "/"
	} else if strings.Contains(".mp3.wav.wma.mp4.mpeg", extName) {
		maxSize = 500 * 1024 * 1024
		filePath = basePath + "media/" + dateStr + "/"
		vstUrl = baseUrl + "media/" + dateStr + "/"
	} else {
		filePath = basePath + "other/" + dateStr + "/"
		vstUrl = baseUrl + "other/" + dateStr + "/"
	}
	if maxSize < header.Size {
		utils.ToJson(w, utils.FileSize)
		return
	}
	// 创建目录
	if _, err = os.Stat(filePath); err != nil || os.IsNotExist(err) {
		if err = os.Mkdir(filePath, os.ModePerm); err != nil {
			logger.Error("上传目录创建失败。" + err.Error())
			utils.ToJson(w, utils.UpPathErr)
			return
		}
	}
	//重命名文件
	newName := time.Now().Format("02150405") + fmt.Sprintf("%08v", rand.Intn(100000000)) + extName
	newFile, err := os.Create(filePath + newName)
	if err != nil {
		logger.Error("文件上传失败。" + err.Error())
		utils.ToJson(w, utils.UploadErr)
		return
	}
	//将文件写到本地
	if _, err = io.Copy(newFile, file); err != nil {
		logger.Error("文件保存失败。" + err.Error())
		utils.ToJson(w, utils.UploadErr)
		return
	}
	err = os.Chmod(newFile.Name(), 0644)
	if err != nil {
		logger.Error("文件赋权失败。" + err.Error())
		utils.ToJson(w, utils.UploadErr)
		return
	}
	fileInfo := make(map[string]string)
	// 如果是图片格式的文件，则进行压缩和生成缩略图
	if strings.Contains(".jpg.png.gif.bmp.jpeg", extName) {
		// 压缩图片，高度设为0
		dstPath += newName
		go func() {
			utils.ImgProc.ThumbImage(filePath+newName, dstPath, 1080, 0)
		}()
		// 生成缩略图
		thumbPath += newName
		utils.ImgProc.ThumbImage(filePath+newName, thumbPath, 240, 240)
		fileInfo["thumb"] = thumbUrl + newName
	}
	fileId := code.NewNanoid(21)
	fileInfo["id"] = fileId
	fileInfo["name"] = fileName
	fileInfo["url"] = vstUrl + newName
	utils.ToJson(w, &utils.ResJson{Code: utils.Succeed.Code, Msg: utils.Succeed.Msg, Data: fileInfo})
}

type FileUrlReq struct {
	Url string `json:"url" v:"文件路径"`
}

// Delete 删除文件
func (c *comm) Delete(r *http.Request, w http.ResponseWriter) {
	req := &FileUrlReq{}
	// 将请求的参数转换为请求结构体
	if err := parse.Parse(r, req); err != "" {
		utils.ToJson(w, &utils.ResJson{Code: utils.ParamErr.Code, Msg: err})
		return
	}
	// 过滤关键参数值中的非法字符
	req.Url = utils.InputFilter(req.Url, true)
	idx := strings.Index(req.Url, "/file")
	realPath := conf.Cfg.AppConf.FilePath + req.Url[idx:]
	strArr := strings.Split(req.Url, "/")
	var fileName string
	if len(strArr) > 1 {
		fileName = strArr[len(strArr)-1]
		nameArr := strings.Split(fileName, ".")
		extName := nameArr[len(nameArr)-1]
		var kind int64 = 2
		if strings.Contains(".jpg.png.gif.bmp.jpeg", extName) {
			kind = 1
		}
		utils.ImgProc.RemoveFile(kind, realPath)
		utils.ToJson(w, utils.Succeed)
		return
	}
	utils.ToJson(w, utils.DelFileErr)
}

// Download 下载文件
func (c *comm) Download(r *http.Request, w http.ResponseWriter) {
	req := &FileUrlReq{}
	// 将请求的参数转换为请求结构体
	if err := parse.Parse(r, req); err != "" {
		utils.ToJson(w, &utils.ResJson{Code: utils.ParamErr.Code, Msg: err})
		return
	}
	// 过滤关键参数值中的非法字符
	req.Url = utils.InputFilter(req.Url, true)
	idx := strings.Index(req.Url, "/file")
	realPath := conf.Cfg.AppConf.FilePath + req.Url[idx:]
	strArr := strings.Split(req.Url, "/")
	var fileName string
	if len(strArr) > 1 {
		fileName = strArr[len(strArr)-1]
	}
	if realPath == "" {
		utils.ToJson(w, utils.DownloadErr)
		return
	}
	if _, err := os.Stat(realPath); err != nil || os.IsNotExist(err) {
		utils.ToJson(w, utils.DownloadErr)
		return
	}
	fb, err := os.ReadFile(realPath)
	if err != nil {
		utils.ToJson(w, utils.DownloadErr)
		return
	}
	w.Header().Add("content-type", "application/octet-stream")
	w.Header().Add("content-disposition", "attachment; filename="+url.QueryEscape(fileName))
	w.Header().Add("Access-Control-Expose-Headers", "content-disposition")
	if _, err = w.Write(fb); err != nil {
		utils.ToJson(w, utils.DownloadErr)
	}
}
